﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace Demo
{
    public enum ExpressionPositionEnum
    {
        None = 0,
        Left = 1,
        Right = 2
    }

    public class ValidationResult
    {
        public bool Success { get; private set; }
        public LambdaExpression Expression { get; set; }

        public ValidationResult(bool success, LambdaExpression expression)
        {
            Success = success;
            Expression = expression;
        }

        public override string ToString()
        {
            return string.Format("Expression: {0}\r\nSuccess: {1}", Expression, Success);
        }
    }

    public class EntityDataValidator
    {
        private static readonly LinqExpressionTreeBuilder linqExpressionTreeBuilder = new LinqExpressionTreeBuilder();

        public ValidationResult Validate(string expression, object instance)
        {
            var lambda = linqExpressionTreeBuilder.Build(expression,instance);
            var validateFunc = lambda.Compile();
            var success =   validateFunc(instance);
            return new ValidationResult(success, lambda);
        }
    }

    public class LinqExpressionTreeBuilder
    {


        private ParameterExpression _parameterExpression;
        private Type _parameterType;

        private static readonly BindingFlags BINDING_FLAGS =
            BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase;

        private static readonly string CLEAR_WHIHT_SPACE_PATTERN = @"\s*";

        private void Init(Type parameterType)
        {
            _parameterType = parameterType;
            _parameterExpression = Expression.Parameter(typeof(object), "instance");
        }

        public Expression<Func<object, bool>> Build(string expression, object instance)
        {
            Init(instance.GetType());
            clearedExpression = Regex.Replace(expression, CLEAR_WHIHT_SPACE_PATTERN, "");
            var converter = new ExpressionConverter();

            var root = converter.CreateBinaryTree(clearedExpression);

            var body = BuildLinqExpression(root, ExpressionPositionEnum.None);
            var lambda = Expression.Lambda<Func<object, bool>>(body, _parameterExpression);

            return lambda;
        }


        private Dictionary<string, ExpressionType> OPERTIONS_MAP = new Dictionary<string, ExpressionType>() {
              {"=", ExpressionType.Equal},
              {"&", ExpressionType.And},
              {"|", ExpressionType.Or}
            };

        public Expression BuildLinqExpression(ExpressionNode node, ExpressionPositionEnum expressionPosition)
        {
            if (node != null)
            {
                if (OPERTIONS_MAP.ContainsKey(node.Value))
                {
                    var left = BuildLinqExpression(node.Left, ExpressionPositionEnum.Left);
                    if (left is MemberExpression)
                    {
                        var parameterLeft = (MemberExpression)left;
                        var t = (PropertyInfo)parameterLeft.Member;

                        var changeData = Convert.ChangeType(node.Right.Value, t.PropertyType, null);
                        node.Right.ChangedValue = changeData;

                    }
                    var right = BuildLinqExpression(node.Right, ExpressionPositionEnum.Right);

                    var expType = OPERTIONS_MAP[node.Value];


                    var exp = Expression.MakeBinary(expType, left, right);
                    return exp;

                }
                else
                {
                    if (expressionPosition == ExpressionPositionEnum.Left)
                    {
                        var prop = _parameterType.GetProperty(node.Value, BINDING_FLAGS);
                        var convertExp = Expression.Convert(_parameterExpression, _parameterType);
                        var propExpression = Expression.MakeMemberAccess(convertExp, prop);
                        return propExpression;
                    }
                    else if (expressionPosition == ExpressionPositionEnum.Right)
                    {
                        var valueExpression = Expression.Constant(node.ChangedValue);
                        return valueExpression;
                    }
                }

            }
            return null;
        }


        public string clearedExpression { get; set; }
    }

    public class ExpressionConverter
    {
        private String[] ConvertStringExpressionToArray(string expression)
        {
            var list = new List<string>();

            var chars = expression.ToCharArray();
            for (var i = 0; i < chars.Length; i++)
            {
                var item = chars[i];
                if (item == '(' || item == ')' || item == '&' || item == '|' || item == '=')
                {
                    list.Add(item.ToString());
                }
                else if (char.IsLetter(item) || char.IsDigit(item) || item == '.')
                {
                    var j = i + 1;
                    var tmp = item.ToString();
                    while (j < chars.Length && (char.IsDigit(chars[j]) || char.IsLetter(chars[j]) || chars[j] == '.'))
                    {
                        tmp += chars[j].ToString();
                        j++;
                    }
                    list.Add(tmp);
                    i = j - 1;
                }
            }

            return list.ToArray();
        }

        public ExpressionNode CreateBinaryTree(string expression)
        {
            var expressionArray = ConvertStringExpressionToArray(expression);
            return CreateBinaryTree(expressionArray);
        }


        public ExpressionNode CreateBinaryTree(string[] expression)
        {
            var opStack = new Stack<string>();
            Queue<string> reversePolish = new Queue<string>();
            for (var i = 0; i < expression.Length; i++)
            {
                var c = expression[i];

                if (isDigitOrLetter(c))
                {
                    reversePolish.Enqueue(c);
                }
                else if (checkIsOp(c))
                {
                    if (c == "(")
                    {
                        opStack.Push(c);
                    }
                    else if (c == ")")
                    {
                        while (opStack.Count > 0)
                        {
                            var op = opStack.Peek();
                            if (op == "(")
                            {
                                opStack.Pop();
                                break;
                            }
                            else
                            {
                                reversePolish.Enqueue(opStack.Pop());
                            }
                        }
                    }
                    else
                    {
                        while (opStack.Count > 0)
                        {
                            if ("(" == opStack.Peek())
                            {
                                opStack.Push(c);
                                break;
                            }
                            else if (isGreat(opStack.Peek(), c))
                            {
                                reversePolish.Enqueue(opStack.Pop());
                            }
                            else if (isEqual(c, opStack.Peek()))
                            {
                                opStack.Push(c);
                                break;
                            }
                        }

                        if (opStack.Count == 0)
                        {
                            opStack.Push(c);
                        }
                    }
                }



            }

            // 将剩余的操作符入队
            while (opStack.Count > 0)
            {
                reversePolish.Enqueue(opStack.Pop());
            }


            var nodeStack = new Stack<ExpressionNode>();

            // 将逆波兰式转化成二叉树
            while (reversePolish.Count > 0)
            {

                var s = reversePolish.Dequeue();
                // 以当前的元素的值新建一个节点
                var node = new ExpressionNode();
                node.Value = s;
                // 如果是数字
                if (isDigitOrLetter(s))
                {

                    nodeStack.Push(node);
                    // 如果是操作符
                }
                else if (checkIsOp(s))
                {

                    //从栈里弹出两个节点作为当前节点的左右子节点
                    var rightNode = nodeStack.Pop();
                    var leftNode = nodeStack.Pop();
                    node.Left = leftNode;
                    node.Right = rightNode;
                    // 入栈
                    nodeStack.Push(node);
                }

            }

            return nodeStack.Pop();
        }

        private bool isDigitOrLetter(string s)
        {
            for (var i = 0; i < s.Length; i++)
            {
                if (Char.IsDigit(s[i]) || char.IsLetter(s[i]))
                {
                    return true;
                }
            }
            return false;
        }

        private static readonly HashSet<string> OP_SETS = new HashSet<string>(){
            "(",")", "|","=","&","+","*","-","/"
        };




        private bool checkIsOp(string c)
        {
            return OP_SETS.Contains(c);
        }


        private bool isGreat(string op1, string op2)
        {

            if (getPriority(op1) > getPriority(op2))

                return true;

            else

                return false;
        }

        private bool isEqual(string op1, string op2)
        {
            if (getPriority(op1) == getPriority(op2))
                return true;
            return false;
        }

        private int getPriority(string op)
        {

            if ("+" == (op) || "-" == op || op == "=")

                return 1;

            else if ("*" == op || "/" == op || op == "&" || op == "|")

                return 2;

            else

                throw new Exception("Unsupported operator!");
        }

    }
    public class ExpressionNode
    {
        public ExpressionNode Left { get; set; }
        public ExpressionNode Right { get; set; }
        public string Value { get; set; }
        public object ChangedValue { get; set; }
    }
}
